home *** CD-ROM | disk | FTP | other *** search
/ Aminet 51 / Aminet 51 (2002)(GTI - Schatztruhe)[!][Oct 2002].iso / Aminet / util / arc / xadmasterdev.lha / xad / Sources / tools / xadUnFile.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-08-20  |  33.1 KB  |  1,087 lines

  1. #ifdef MULTIFILE
  2.   #define NAME         "xadUnFileM"
  3. #else
  4.   #define NAME         "xadUnFile"
  5. #endif
  6. #define DISTRIBUTION "(Freeware) "
  7. #define REVISION     "25"
  8. #define DATE         "10.03.2002"
  9.  
  10. /* Programmheader
  11.  
  12.     Name:        xadUnFile
  13.     Author:        SDI
  14.     Distribution:    Freeware
  15.     Description:    dearchives file archives
  16.     Compileropts:    -
  17.     Linkeropts:    -gsi -l amiga
  18.  
  19.  1.0   13.09.98 : first version
  20.  1.1   18.11.98 : added FILE parameter and directory creation
  21.  1.2   03.02.99 : added corrupt message
  22.  1.3   07.02.99 : added missing "group crunched" handling
  23.  1.4   16.03.99 : errors no longer abort decrunching
  24.  1.5   21.06.99 : added support for multiple input files and renaming
  25.  1.6   04.07.99 : bug fix
  26.  1.7   18.07.99 : added SHOWPROT, reduced status prints, added QUIT,
  27.     splitted in xadUnFile and xadUnFileM
  28.  1.8   19.08.99 : also strips "/" at name start for NOABS
  29.  1.9   15.09.99 : forgot to set date/comment/bits for directories
  30.  1.10  28.09.99 : added NAMESIZE keyword
  31.  1.11  01.11.99 : added SFS keyword
  32.  1.12  07.12.99 : added NOKILLPART keyword
  33.  1.13  17.12.99 : added VERBOSE mode
  34.  1.14  14.01.00 : added DARC, ENTRY, DIMG
  35.  1.15  12.02.00 : added auto DIMG and pattern support
  36.  1.16  05.03.00 : bug fixes
  37.  1.17  19.03.00 : support for XADFIF_NOUNCRUNCHSIZE
  38.  1.18  28.05.00 : added final report
  39.  1.19  04.06.00 : added multi-filesystem support
  40.  1.20  27.07.00 : fixed multi-filesystem support for empty fs parts
  41.  1.21  28.01.01 : returns RETURN_ERROR if errors occured
  42.  1.22  25.02.01 : GetInfo supports progress hook now
  43.  1.23  12.08.01 : shows unix devices info
  44.  1.24  17.11.01 : added indisk-support
  45.  1.25  10.03.02 : now aborts for disk I/O errors
  46. */
  47.  
  48. #include <proto/xadmaster.h>
  49. #include <proto/exec.h>
  50. #include <proto/dos.h>
  51. #include <exec/memory.h>
  52. #include <dos/dosasl.h>
  53. #include <utility/hooks.h>
  54. #include "SDI_version.h"
  55. #include "SDI_compiler.h"
  56. #define SDI_TO_ANSI
  57. #include "SDI_ASM_STD_protos.h"
  58.  
  59. struct xadMasterBase *    xadMasterBase = 0;
  60. struct DosLibrary *     DOSBase = 0;
  61. struct ExecBase *     SysBase  = 0;
  62.  
  63. #define MINPRINTSIZE    51200    /* 50KB */
  64. #define NAMEBUFSIZE    512
  65. #define PATBUFSIZE    (NAMEBUFSIZE*2+10)
  66.  
  67. #ifdef MULTIFILE
  68. #define PARAM    "FROM/A/M,DEST=DESTDIR/K,PASSWORD/K,FILE/K,"    \
  69.         "NAMESIZE/K/N,FFS=OFS/S,SFS/S,"            \
  70.         "INFO=LIST/S,Q=QUIET/S,AM=ASKMAKEDIR/S,"    \
  71.         "OW=OVERWRITE/S,SP=SHOWPROT/S,VERBOSE/S,"    \
  72.         "DARC=DISKARCHIVE/S,ENTRY/K/N,DIMG=DISKIMAGE/S,"\
  73.         "NA=NOABS/S,NC=NOCOMMENT/S,ND=NODATE/S,"    \
  74.         "NE=NOEXTERN/S,NKP=NOKILLPART/S,NP=NOPROT/S,"    \
  75.         "NT=NOTREE/S"
  76. #else
  77. #define PARAM    "FROM/A,DEST=DESTDIR,PASSWORD/K,FILE/M,"    \
  78.         "NAMESIZE/K/N,FFS=OFS/S,SFS/S,"            \
  79.         "INFO=LIST/S,Q=QUIET/S,AM=ASKMAKEDIR/S,"    \
  80.         "OW=OVERWRITE/S,SP=SHOWPROT/S,VERBOSE/S,"    \
  81.         "DARC=DISKARCHIVE/S,ENTRY/K/N,DIMG=DISKIMAGE/S,"\
  82.         "NA=NOABS/S,NC=NOCOMMENT/S,ND=NODATE/S,"    \
  83.         "NE=NOEXTERN/S,NKP=NOKILLPART/S,NP=NOPROT/S,"    \
  84.         "NT=NOTREE/S"
  85. #endif
  86.  
  87. #ifdef MULTIFILE
  88. #define OPTIONS1 \
  89.   "FROM       The input archive file(s)\n"                \
  90.   "DESTDIR    The destination directory, not needed with INFO\n"    \
  91.   "PASSWORD   A password for encrypted archives\n"            \
  92.   "FILE       Filename (with patterns) to be extracted\n"
  93. #else
  94. #define OPTIONS1 \
  95.   "FROM       The input archive file (no patterns allowed)\n"        \
  96.   "DESTDIR    The destination directory, not needed with INFO\n"    \
  97.   "PASSWORD   A password for encrypted archives\n"            \
  98.   "FILE       Filename(s) (with patterns) to be extracted\n"
  99. #endif
  100.  
  101. #define OPTIONS2 \
  102.   "NAMESIZE   Names with more characters result in rename request\n"    \
  103.   "FFS=OFS    Sets NAMESIZE to 30\n"                    \
  104.   "SFS        Sets NAMESIZE to 100\n"                    \
  105.   "INFO       Shows archive information without extracting\n"        \
  106.   "QUIET      Turns of progress report and user interaction\n"        \
  107.   "ASKMAKEDIR You get asked before a directory is created\n"        \
  108.   "OVERWRITE  Files are overwritten without asking\n"            \
  109.   "SHOWPROT   Show protection information with LIST\n"            \
  110.   "VERBOSE    Print some more information with INFO\n"            \
  111.   "DARC       input file is an disk archive\n"                \
  112.   "ENTRY      entry number for DARC, if not the first one\n"        \
  113.   "DIMG          input file is an disk image (ADF file)\n"            \
  114.   "NOABS      Do not extract absolute path name parts\n"        \
  115.   "NOCOMMENT  No filenote comments are extracted or displayed\n"    \
  116.   "NODATE     Creation date information gets not extracted\n"        \
  117.   "NOEXTERN   Turns off usage of external clients\n"            \
  118.   "NOKILLPART Do not delete partial or corrupt output files.\n"        \
  119.   "NOPROT     Protection information gets not extracted\n"        \
  120.   "NOTREE     Files are extracted without subdirectories\n"
  121.  
  122. struct xHookArgs {
  123.   STRPTR name;
  124.   ULONG extractmode;
  125.   ULONG flags;
  126.   ULONG finish;
  127.   ULONG lastprint;
  128. };
  129.  
  130. struct Args {
  131. #ifdef MULTIFILE
  132.   STRPTR * from;
  133.   STRPTR   destdir;
  134.   STRPTR   password;
  135.   STRPTR   file;
  136. #else
  137.   STRPTR   from;
  138.   STRPTR   destdir;
  139.   STRPTR   password;
  140.   STRPTR * file;
  141. #endif
  142.   LONG *   namesize;
  143.   ULONG    ffs;
  144.   ULONG       sfs;
  145.   ULONG    info;
  146.   ULONG    quiet;
  147.   ULONG    askmakedir;
  148.   ULONG    overwrite;
  149.   ULONG       showprot;
  150.   ULONG    verbose;
  151.   ULONG    diskarchive;
  152.   LONG *   entry;
  153.   ULONG    diskimage;
  154.   ULONG    noabs;
  155.   ULONG    nocomment;
  156.   ULONG    nodate;
  157.   ULONG    noextern;
  158.   ULONG       nokillpart;
  159.   ULONG    noprot;
  160.   ULONG    notree;
  161. };
  162.  
  163. ASM(ULONG) progrhook(REG(a0, struct Hook *),
  164.   REG(a1, struct xadProgressInfo *));
  165.  
  166. void ShowProt(ULONG i);
  167. LONG CheckNameSize(STRPTR name, ULONG size);
  168. void CalcPercent(ULONG cr, ULONG ucr, ULONG *p1, ULONG *p2);
  169.  
  170. #ifndef MULTIFILE
  171.   LONG CheckName(STRPTR *pat, STRPTR name);
  172. #else
  173.   STRPTR *GetNames(STRPTR *names);
  174. #endif
  175.  
  176. ULONG start(void)
  177. {
  178.   ULONG ret = RETURN_FAIL, numerr = 0;
  179.   struct DosLibrary *dosbase;
  180.  
  181.   SysBase = (*((struct ExecBase **) 4));
  182.   { /* test for WB and reply startup-message */
  183.     struct Process *task;
  184.     if(!(task = (struct Process *) FindTask(0))->pr_CLI)
  185.     {
  186.       WaitPort(&task->pr_MsgPort);
  187.       Forbid();
  188.       ReplyMsg(GetMsg(&task->pr_MsgPort));
  189.       return RETURN_FAIL;
  190.     }
  191.   }
  192.  
  193.   if((dosbase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
  194.   {
  195.     LONG err = 0;
  196.     struct xadMasterBase *xadmasterbase;
  197.  
  198.     DOSBase = dosbase;
  199.     if((xadmasterbase = (struct xadMasterBase *)
  200.     OpenLibrary("xadmaster.library", 11)))
  201.     {
  202.       struct Args args;
  203.       struct RDArgs *rda;
  204.       
  205.       memset(&args, 0, sizeof(struct Args));
  206.       xadMasterBase = xadmasterbase;
  207.  
  208.       if((rda = (struct RDArgs *) AllocDosObject(DOS_RDARGS, 0)))
  209.       {
  210.         rda->RDA_ExtHelp = OPTIONS1 OPTIONS2;
  211.  
  212.         if(ReadArgs(PARAM, (LONG *) &args, rda))
  213.         {
  214.           LONG namesize = 0;
  215.       struct Hook prhook;
  216.       UBYTE filename[NAMEBUFSIZE];
  217.       struct xHookArgs xh;
  218.  
  219. #ifdef MULTIFILE
  220.       STRPTR *argstring;
  221. #endif
  222.  
  223.       xh.name = filename;
  224.       xh.flags = xh.finish = xh.lastprint = xh.extractmode = 0;
  225.  
  226.       /* Note! The hook may change the filename!!! */
  227.           memset(&prhook, 0, sizeof(struct Hook));
  228.           prhook.h_Entry = (ULONG (*)()) progrhook;
  229.           prhook.h_Data = &xh;
  230.           
  231.           if(args.namesize && *args.namesize > 0)
  232.             namesize = *args.namesize;
  233.           else if(args.ffs)
  234.             namesize = 30;
  235.           else if(args.sfs)
  236.             namesize = 100;
  237.  
  238. #ifdef MULTIFILE
  239.       if(!(args.from = argstring = GetNames(args.from))) /* correct the argument list */
  240.             ;
  241.           else if(!(*argstring))
  242.             SetIoErr(ERROR_REQUIRED_ARG_MISSING);
  243.       else /* comes together with following if! */
  244. #endif
  245.       if(args.destdir || args.info)
  246.       {
  247.             ULONG numfile = 0, numdir = 0;
  248.             struct xadDeviceInfo *dvi = 0;
  249.         struct xadArchiveInfo *ai;
  250.             struct TagItem ti[5];
  251.             struct TagItem ti2[5];
  252.             LONG loop = 2;
  253.             
  254.             ti[1].ti_Tag = XAD_NOEXTERN;
  255.         ti[1].ti_Data = args.noextern;
  256.         ti[2].ti_Tag = args.password ? XAD_PASSWORD : TAG_IGNORE;
  257.         ti[2].ti_Data = (ULONG) args.password;
  258.         ti[3].ti_Tag = args.entry ? XAD_ENTRYNUMBER : TAG_IGNORE;
  259.         ti[3].ti_Data = args.entry ? *args.entry : 1;
  260.         ti[4].ti_Tag = TAG_DONE;
  261.  
  262.         ti2[1].ti_Tag = XAD_NOEMPTYERROR;
  263.         ti2[1].ti_Data = TRUE;
  264.         ti2[2].ti_Tag = args.quiet ? TAG_IGNORE : XAD_PROGRESSHOOK;
  265.         ti2[2].ti_Data = (ULONG) &prhook;
  266.         ti2[3].ti_Tag = TAG_DONE;
  267.         ti2[4].ti_Tag = TAG_DONE; /* needed later for loop */
  268.  
  269.         if((ai = (struct xadArchiveInfo *)
  270.         xadAllocObjectA(XADOBJ_ARCHIVEINFO, 0)))
  271.         {
  272. #ifdef MULTIFILE
  273.           if(*(args.from+1))
  274.           {
  275.             struct xadSplitFile *sf = 0, *sf2, *sf0 = 0;
  276.         while(*args.from && !err)
  277.         {
  278.           if((sf2 = xadAllocObjectA(XADOBJ_SPLITFILE, 0)))
  279.           {
  280.             if(sf)
  281.             {
  282.               sf->xsf_Next = sf2; sf = sf2;
  283.             }
  284.             else
  285.               sf0 = sf = sf2;
  286.             sf->xsf_Type = XAD_INFILENAME;
  287.             sf->xsf_Data = (ULONG) *(args.from++);
  288.           }
  289.           else
  290.             err = XADERR_NOMEMORY;
  291.         }
  292.             if(!err)
  293.             {
  294.               if(args.diskarchive)
  295.               {
  296.                 ti[0].ti_Tag = XAD_INSPLITTED;
  297.                 ti[0].ti_Data = (ULONG) sf0;
  298.  
  299.             ti2[0].ti_Tag = XAD_INDISKARCHIVE;
  300.             ti2[0].ti_Data = (ULONG) ti;
  301.                 if((err = xadGetDiskInfoA(ai, ti2)))
  302.                 {
  303.                   ti2[0].ti_Tag = XAD_INSPLITTED;
  304.                   ti2[0].ti_Data = (ULONG) sf0;
  305.                   if(!xadGetDiskInfoA(ai, ti2))
  306.                     err = 0;
  307.                 }
  308.               }
  309.               else if(args.diskimage)
  310.               {
  311.                 ti2[0].ti_Tag = XAD_INSPLITTED;
  312.                 ti2[0].ti_Data = (ULONG) sf0;
  313.                 err = xadGetDiskInfoA(ai, ti2);
  314.               }
  315.               else
  316.               {
  317.                 err = xadGetInfo(ai, XAD_INSPLITTED, sf0, XAD_NOEXTERN,
  318.                 args.noextern, args.password ? XAD_PASSWORD : TAG_IGNORE,
  319.                 args.password, args.quiet ? TAG_IGNORE : XAD_PROGRESSHOOK,
  320.                 &prhook, TAG_DONE);
  321.                 --loop;
  322.               }
  323.             }
  324.             while(sf0)
  325.             {
  326.               sf2 = sf0; sf0 = sf0->xsf_Next;
  327.               xadFreeObjectA(sf2, 0);
  328.             }
  329.           }
  330.           else if(args.diskarchive)
  331.           {
  332.             ti[0].ti_Tag = XAD_INFILENAME;
  333.             ti[0].ti_Data = (ULONG) *args.from;
  334.  
  335.         ti2[0].ti_Tag = XAD_INDISKARCHIVE;
  336.         ti2[0].ti_Data = (ULONG) ti;
  337.             if((err = xadGetDiskInfoA(ai, ti2)))
  338.             {
  339.               ti2[0].ti_Tag = XAD_INFILENAME;
  340.               ti2[0].ti_Data = (ULONG) *args.from;
  341.               if(!xadGetDiskInfoA(ai, ti2))
  342.                 err = 0;
  343.             }
  344.           }
  345.           else if(args.diskimage)
  346.           {
  347.             if(*args.from[strlen(*args.from)-1] == ':')
  348.             {
  349.               if((dvi = (struct xadDeviceInfo *) xadAllocObjectA(XADOBJ_DEVICEINFO, 0)))
  350.               {
  351.                 *args.from[strlen(*args.from)-1] = 0; /* strip ':' */
  352.                 dvi->xdi_DOSName = *args.from;
  353.                 ti2[0].ti_Tag = XAD_INDEVICE;
  354.                 ti2[0].ti_Data = (ULONG) dvi;
  355.                 err = xadGetDiskInfoA(ai, ti2);
  356. /*                *args.from[strlen(*args.from)] = ':'; */
  357.               }
  358.               else
  359.                 err = XADERR_NOMEMORY;
  360.             }
  361.             else
  362.             {
  363.               ti2[0].ti_Tag = XAD_INFILENAME;
  364.               ti2[0].ti_Data = (ULONG) *args.from;
  365.               err = xadGetDiskInfoA(ai, ti2);
  366.             }
  367.           }
  368.           else
  369.           {
  370.             err = xadGetInfo(ai, XAD_INFILENAME, *args.from,
  371.             XAD_NOEXTERN, args.noextern, args.password ? XAD_PASSWORD :
  372.             TAG_IGNORE, args.password, args.quiet ? TAG_IGNORE : XAD_PROGRESSHOOK,
  373.             &prhook, TAG_DONE);
  374.             --loop;
  375.           }
  376. #else
  377.           if(args.diskarchive)
  378.           { 
  379.             ti[0].ti_Tag = XAD_INFILENAME;
  380.             ti[0].ti_Data = (ULONG) args.from;
  381.  
  382.         ti2[0].ti_Tag = XAD_INDISKARCHIVE;
  383.         ti2[0].ti_Data = (ULONG) ti;
  384.             if((err = xadGetDiskInfoA(ai, ti2)))
  385.             {
  386.               ti2[0].ti_Tag = XAD_INFILENAME;
  387.               ti2[0].ti_Data = (ULONG) args.from;
  388.               if(!xadGetDiskInfoA(ai, ti2))
  389.                 err = 0;
  390.             }
  391.           }
  392.           else if(args.diskimage)
  393.           {
  394.             if(args.from[strlen(args.from)-1] == ':')
  395.             {
  396.               if((dvi = (struct xadDeviceInfo *) xadAllocObjectA(XADOBJ_DEVICEINFO, 0)))
  397.               {
  398.                 args.from[strlen(args.from)-1] = 0; /* strip ':' */
  399.                 dvi->xdi_DOSName = args.from;
  400.                 ti2[0].ti_Tag = XAD_INDEVICE;
  401.                 ti2[0].ti_Data = (ULONG) dvi;
  402.                 err = xadGetDiskInfoA(ai, ti2);
  403. /*                args.from[strlen(args.from)] = ':'; */
  404.               }
  405.               else
  406.                 err = XADERR_NOMEMORY;
  407.             }
  408.             else
  409.             {
  410.               ti2[0].ti_Tag = XAD_INFILENAME;
  411.               ti2[0].ti_Data = (ULONG) args.from;
  412.               err = xadGetDiskInfoA(ai, ti2);
  413.             }
  414.           }
  415.           else
  416.           {
  417.             err = xadGetInfo(ai, XAD_INFILENAME, args.from,
  418.             XAD_NOEXTERN, args.noextern, args.password ? XAD_PASSWORD :
  419.             TAG_IGNORE, args.password, args.quiet ? TAG_IGNORE :
  420.             XAD_PROGRESSHOOK, &prhook, TAG_DONE);
  421.             --loop;
  422.           }
  423. #endif
  424.  
  425.           while(!err && loop)
  426.           {
  427.             if(ai->xai_Flags & XADAIF_FILECORRUPT)
  428.               Printf("!!! The archive file has some corrupt data. !!!\n");
  429.             if(args.info)
  430.             {
  431.               struct xadFileInfo *xfi;
  432.               ULONG grsize = 0;
  433.               if(ai->xai_Client)
  434.                 Printf("ClientName: %s\n", ai->xai_Client->xc_ArchiverName);
  435.           Printf("Size     CrndSize Ratio Date       Time     %s%sName\n",
  436.           args.verbose ? "Info           " : "",args.showprot ? "Protection       " : "");
  437.  
  438.               xfi = ai->xai_FileInfo;
  439.               while(xfi && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
  440.               {
  441.                 if(!(xfi->xfi_Flags & XADFIF_GROUPED))
  442.                   grsize = 0;
  443.             if(xfi->xfi_Flags & XADFIF_DIRECTORY)
  444.             {
  445.                   Printf("   <dir>    <dir>       %02ld.%02ld.%04ld %02ld:%02ld:%02ld ",
  446.                   xfi->xfi_Date.xd_Day, xfi->xfi_Date.xd_Month,
  447.                   xfi->xfi_Date.xd_Year, xfi->xfi_Date.xd_Hour,
  448.                   xfi->xfi_Date.xd_Minute, xfi->xfi_Date.xd_Second);
  449.               if(args.verbose)
  450.                 Printf("%-15s", xfi->xfi_EntryInfo);
  451.                   if(args.showprot)
  452.                     ShowProt(xfi->xfi_Protection);
  453.                   Printf("%s\n", args.notree ? FilePart(xfi->xfi_FileName) :
  454.                   xfi->xfi_FileName);
  455.                 }
  456.             else if(xfi->xfi_Flags & XADFIF_GROUPED)
  457.             {
  458.                   Printf("%8lu   merged  n/a  %02ld.%02ld.%04ld %02ld:%02ld:%02ld ",
  459.                   xfi->xfi_Size, xfi->xfi_Date.xd_Day, xfi->xfi_Date.xd_Month,
  460.                   xfi->xfi_Date.xd_Year, xfi->xfi_Date.xd_Hour,
  461.                   xfi->xfi_Date.xd_Minute, xfi->xfi_Date.xd_Second);
  462.               if(args.verbose)
  463.                 Printf("%-15s", xfi->xfi_EntryInfo);
  464.                   if(args.showprot)
  465.                     ShowProt(xfi->xfi_Protection);
  466.                   Printf("%s\n", args.notree ? FilePart(xfi->xfi_FileName) :
  467.                   xfi->xfi_FileName);
  468.                   grsize += xfi->xfi_Size;
  469.                   if(xfi->xfi_Flags & XADFIF_ENDOFGROUP)
  470.                   {
  471.                 ULONG i, j;
  472.               
  473.             CalcPercent(xfi->xfi_GroupCrSize, grsize, &i, &j);
  474.                     Printf("%8ld %8ld %2ld.%1ld%%\n", grsize, xfi->xfi_GroupCrSize, i, j);
  475.                     grsize = 0;
  476.                   }
  477.             }
  478.             else if(xfi->xfi_Flags & XADFIF_NOUNCRUNCHSIZE)
  479.             {
  480.                   Printf("<nosize> %8lu       %02ld.%02ld.%04ld %02ld:%02ld:%02ld ",
  481.                   xfi->xfi_CrunchSize, xfi->xfi_Date.xd_Day, xfi->xfi_Date.xd_Month,
  482.                   xfi->xfi_Date.xd_Year, xfi->xfi_Date.xd_Hour,
  483.                   xfi->xfi_Date.xd_Minute, xfi->xfi_Date.xd_Second);
  484.               if(args.verbose)
  485.                 Printf("%-15s", xfi->xfi_EntryInfo);
  486.                   if(args.showprot)
  487.                     ShowProt(xfi->xfi_Protection);
  488.                   Printf("%s\n", args.notree ? FilePart(xfi->xfi_FileName) :
  489.                   xfi->xfi_FileName);
  490.                 }
  491.             else
  492.             {
  493.               ULONG i, j;
  494.               
  495.               CalcPercent(xfi->xfi_CrunchSize, xfi->xfi_Size, &i, &j);
  496.                   Printf("%8lu %8lu %2ld.%1ld%% %02ld.%02ld.%04ld %02ld:%02ld:%02ld ",
  497.                   xfi->xfi_Size, xfi->xfi_CrunchSize, i, j,
  498.                   xfi->xfi_Date.xd_Day, xfi->xfi_Date.xd_Month,
  499.                   xfi->xfi_Date.xd_Year, xfi->xfi_Date.xd_Hour,
  500.                   xfi->xfi_Date.xd_Minute, xfi->xfi_Date.xd_Second);
  501.               if(args.verbose)
  502.                 Printf("%-15s", xfi->xfi_EntryInfo);
  503.                   if(args.showprot)
  504.                     ShowProt(xfi->xfi_Protection);
  505.                   Printf("%s\n", args.notree ? FilePart(xfi->xfi_FileName) :
  506.                   xfi->xfi_FileName);
  507.                 }
  508.                 if(xfi->xfi_Flags & XADFIF_LINK)
  509.                   Printf("link: %s\n", xfi->xfi_LinkName);
  510.                 if(!args.nocomment)
  511.                 {
  512.                   if(xfi->xfi_Comment)
  513.                     Printf(": %s\n", xfi->xfi_Comment);
  514.                   if(xfi->xfi_Special && xfi->xfi_Special->xfis_Type ==
  515.                   XADSPECIALTYPE_UNIXDEVICE)
  516.                     Printf(": Unix %s device (%3ld | %3ld)\n", xfi->xfi_FileType ==
  517.                     XADFILETYPE_UNIXBLOCKDEVICE ? "block" : "character",
  518.                     xfi->xfi_Special->xfis_Data.xfis_UnixDevice.xfis_MajorVersion,
  519.                     xfi->xfi_Special->xfis_Data.xfis_UnixDevice.xfis_MinorVersion);
  520.                   if(xfi->xfi_FileType == XADFILETYPE_UNIXFIFO)
  521.                     Printf(": Unix named pipe\n");
  522.                 }
  523. #ifdef DEBUG
  524.             if(xfi->xfi_Flags)
  525.             {
  526.                   Printf("Flags: ");
  527.                   if(xfi->xfi_Flags & XADFIF_CRYPTED)
  528.                     Printf("XADFIF_CRYPTED ");
  529.                   if(xfi->xfi_Flags & XADFIF_DIRECTORY)
  530.                     Printf("XADFIF_DIRECTORY ");
  531.                   if(xfi->xfi_Flags & XADFIF_LINK)
  532.                     Printf("XADFIF_LINK ");
  533.                   if(xfi->xfi_Flags & XADFIF_INFOTEXT)
  534.                     Printf("XADFIF_INFOTEXT ");
  535.                   if(xfi->xfi_Flags & XADFIF_GROUPED)
  536.                     Printf("XADFIF_GROUPED ");
  537.                   if(xfi->xfi_Flags & XADFIF_ENDOFGROUP)
  538.                     Printf("XADFIF_ENDOFGROUP ");
  539.                   if(xfi->xfi_Flags & XADFIF_NODATE)
  540.                     Printf("XADFIF_NODATE ");
  541.                   Printf("\n");
  542.                 }
  543. #endif
  544.                 if(xfi->xfi_Flags & XADFIF_CRYPTED)
  545.                   Printf("The entry is encrypted.\n");
  546.                 if(xfi->xfi_Flags & XADFIF_PARTIALFILE)
  547.                   Printf("The entry is no complete file.\n");
  548.                 xfi = xfi->xfi_Next;
  549.               }
  550.               ret = 0;
  551.             }
  552.             else
  553.             {
  554.           struct xadFileInfo *fi;
  555. #ifdef MULTIFILE
  556.             UBYTE parsebuf[PATBUFSIZE];
  557. #endif
  558.           ret = 0;
  559.           fi = ai->xai_FileInfo;
  560.  
  561. #ifdef MULTIFILE
  562.             if(!args.file || ParsePatternNoCase(args.file, parsebuf, PATBUFSIZE) >= 0)
  563. #endif
  564.             {
  565.             while(fi && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C) && !xh.finish)
  566.             {
  567. #ifdef MULTIFILE
  568.               if(!args.file || MatchPatternNoCase(parsebuf, args.notree ?
  569.               FilePart(fi->xfi_FileName) : fi->xfi_FileName))
  570. #else
  571.               if(!args.file || CheckName(args.file, args.notree ?
  572.               FilePart(fi->xfi_FileName) : fi->xfi_FileName))
  573. #endif
  574.               {
  575.                 CopyMem(args.destdir, filename, strlen(args.destdir)+1);
  576.             if(stricmp(args.destdir, "NIL:"))
  577.             {
  578.                   if(args.notree)
  579.                     AddPart(filename, FilePart(fi->xfi_FileName), NAMEBUFSIZE);
  580.                   else if(!args.noabs)
  581.                     AddPart(filename, fi->xfi_FileName, NAMEBUFSIZE);
  582.                   else
  583.                   {
  584.                     STRPTR fname = filename, f;
  585.  
  586.                 if(*args.destdir)
  587.                 {
  588.                       fname += strlen(args.destdir)-1;
  589.                       if(*fname != ':' && *fname != '/')
  590.                         *(++fname) = '/';
  591.                       ++fname;
  592.                     }
  593.                 for(f = fi->xfi_FileName; *f == '/' || *f == ':'; ++f)
  594.                   ;
  595.                     for(; *f; ++f)
  596.                       *(fname++) = *f == ':' ? '/' : *f;
  597.                     *fname = 0;
  598.                   }
  599.                 }
  600.                 if(fi->xfi_Flags & XADFIF_LINK)
  601.                 {
  602.                   if(!args.quiet)
  603.                     Printf("Skipped Link\n");
  604.                 }
  605.                 else if(fi->xfi_Flags & XADFIF_DIRECTORY)
  606.                 {
  607.                   if(!args.notree)
  608.                   {
  609.                     BPTR a;
  610.                     LONG err = 0, i = 0;
  611.                     UBYTE r;
  612.                 ++numdir;
  613.                     while(filename[i] && !err)
  614.                     {
  615.                         for(;filename[i] && filename[i] != '/'; ++i)
  616.                         ;
  617.                         r = filename[i];
  618.                         filename[i] = 0;
  619.                   if((a = Lock(filename, SHARED_LOCK)))
  620.                           UnLock(a);
  621.                       else if((a = CreateDir(filename)))
  622.                             UnLock(a);
  623.                         else
  624.                             err = 1;
  625.                           filename[i++] = r;
  626.                     }
  627.                     if(!args.quiet)
  628.                     {
  629.                       if(err)
  630.                       {
  631.                         Printf("failed to create directory '%s'\n", fi->xfi_FileName);
  632.                         ++numerr;
  633.                       }
  634.                       else
  635.                         Printf("Created directory   : %s\n", filename);
  636.                     }
  637.                         if(!err)
  638.                         {
  639.                       struct DateStamp d;
  640.  
  641.                           if(!args.nodate && !(fi->xfi_Flags & XADFIF_NODATE)
  642.                           && !xadConvertDates(XAD_DATEXADDATE, &fi->xfi_Date,
  643.                           XAD_GETDATEDATESTAMP, &d, TAG_DONE))
  644.                             SetFileDate(filename, &d);
  645.                           if(!args.noprot)
  646.                             SetProtection(filename, fi->xfi_Protection);
  647.                           if(fi->xfi_Comment && !args.nocomment)
  648.                             SetComment(filename, fi->xfi_Comment);
  649.                           /* SetOwner ??? */
  650.                         }
  651.                   }
  652.                 } 
  653.                 else
  654.                 {
  655.                   struct DateStamp d;
  656.  
  657.               if(namesize)
  658.                 xh.finish = CheckNameSize(FilePart(filename), namesize);
  659.  
  660.               if(!xh.finish)
  661.               {
  662.                 LONG e;
  663.  
  664.                 ++numfile;
  665.  
  666.                             xh.extractmode = 1;
  667.                 if(args.diskimage || args.diskarchive)
  668.                       e = xadDiskFileUnArc(ai, XAD_OUTFILENAME, filename,
  669.                           XAD_ENTRYNUMBER, fi->xfi_EntryNumber, XAD_MAKEDIRECTORY,
  670.                           !args.askmakedir, XAD_OVERWRITE, args.overwrite,
  671.                           XAD_NOKILLPARTIAL, args.nokillpart,  args.quiet ? TAG_IGNORE :
  672.                           XAD_PROGRESSHOOK, &prhook, TAG_DONE);
  673.                     else
  674.                       e = xadFileUnArc(ai, XAD_OUTFILENAME, filename,
  675.                           XAD_ENTRYNUMBER, fi->xfi_EntryNumber, XAD_MAKEDIRECTORY,
  676.                           !args.askmakedir, XAD_OVERWRITE, args.overwrite,
  677.                           XAD_NOKILLPARTIAL, args.nokillpart,  args.quiet ? TAG_IGNORE :
  678.                           XAD_PROGRESSHOOK, &prhook, TAG_DONE);
  679.                             xh.extractmode = 0;
  680.  
  681.                         if(!e)
  682.                         {
  683.                           if(!args.nodate && !(fi->xfi_Flags & XADFIF_NODATE)
  684.                           && !xadConvertDates(XAD_DATEXADDATE, &fi->xfi_Date,
  685.                           XAD_GETDATEDATESTAMP, &d, TAG_DONE))
  686.                             SetFileDate(filename, &d);
  687.                           if(!args.noprot)
  688.                             SetProtection(filename, fi->xfi_Protection);
  689.                           if(fi->xfi_Comment && !args.nocomment)
  690.                             SetComment(filename, fi->xfi_Comment);
  691.                           /* SetOwner ??? */
  692.                         }
  693.                         else
  694.                           ++numerr;
  695.                         /* IO-errors, abort */
  696.                         if(e == XADERR_INPUT || e == XADERR_OUTPUT)
  697.                           xh.finish = 1;
  698.                       }
  699.                     }
  700.                   }
  701.                   fi = fi->xfi_Next;
  702.                 }
  703.               }
  704.             }
  705.             ti2[3].ti_Tag = XAD_STARTCLIENT;
  706.             ti2[3].ti_Data = (ULONG) ai->xai_Client->xc_Next;
  707.             xadFreeInfo(ai);
  708.             if(--loop)
  709.             {
  710.               loop = 0;
  711.               if(ti2[3].ti_Data)
  712.               {
  713.             xadFreeObjectA(ai, 0); /* realloc ai structure */
  714.                 if((ai = (struct xadArchiveInfo *) xadAllocObjectA(XADOBJ_ARCHIVEINFO, 0)))
  715.                 {
  716.                   if(!xadGetDiskInfoA(ai, ti2))
  717.                     loop = 2;
  718.                 }
  719.               }
  720.             }
  721.         if(!args.info && !loop && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
  722.         {
  723.           Printf("Processed");
  724.           if(numfile)
  725.             Printf(" %ld file%s%s", numfile, numfile == 1 ? "" : "s", numdir ? " and" : "");
  726.           if(numdir)
  727.             Printf(" %ld director%s", numdir, numdir == 1 ? "y" : "ies");
  728.           if(!numfile && !numdir)
  729.             Printf(" nothing");
  730.           if(numerr)
  731.             Printf(", %ld error%s", numerr, numerr == 1 ? "" : "s");
  732.           Printf(".\n");
  733.         }
  734.           } /* xadGetInfo, loop */
  735.  
  736.           if(ai)
  737.             xadFreeObjectA(ai, 0);
  738.           if(dvi)
  739.             xadFreeObjectA(dvi, 0);
  740.             } /* xadAllocObject */
  741.           }
  742.           else
  743.             SetIoErr(ERROR_REQUIRED_ARG_MISSING);
  744.  
  745. #ifdef MULTIFILE
  746.       if(argstring)
  747.         FreeVec(argstring);
  748. #endif
  749.  
  750.           FreeArgs(rda);
  751.         } /* ReadArgs */
  752.         FreeDosObject(DOS_RDARGS, rda);
  753.       } /* AllocDosObject */
  754.  
  755.       if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
  756.         SetIoErr(ERROR_BREAK);
  757.  
  758.       if(!args.quiet)
  759.       {
  760.         if(err)
  761.       Printf("An error occured: %s\n", xadGetErrorText(err));
  762.         else if(ret)
  763.           PrintFault(IoErr(), 0);
  764.       }
  765.  
  766.       CloseLibrary((struct Library *) xadmasterbase);
  767.     } /* OpenLibrary xadmaster */
  768.     else
  769.       Printf("Could not open xadmaster.library\n");
  770.     CloseLibrary((struct Library *) dosbase);
  771.   } /* OpenLibrary dos */
  772.  
  773.   if(!ret && numerr)
  774.     ret = RETURN_ERROR;
  775.  
  776.   return ret;
  777. }
  778.  
  779. /* Because of SAS-err, this cannot be SAVEDS */
  780. ASM(ULONG) progrhook(REG(a0, struct Hook *hook),
  781. REG(a1, struct xadProgressInfo *pi))
  782. {
  783.   ULONG ret = 0;
  784.   STRPTR name = ((struct xHookArgs *) (hook->h_Data))->name;
  785.  
  786.   switch(pi->xpi_Mode)
  787.   {
  788.   case XADPMODE_ASK:
  789.     ret |= ((struct xHookArgs *) (hook->h_Data))->flags;
  790.     if((pi->xpi_Status & XADPIF_OVERWRITE) && !(ret & XADPIF_OVERWRITE))
  791.     {
  792.       LONG r;
  793.  
  794.       Printf("File '%s' already exists, overwrite? (Y|A|S|\033[1mN\033[0m|Q|R): ",
  795.       pi->xpi_FileName);
  796.       Flush(Output());
  797.       SetMode(Input(), TRUE);
  798.       r = FGetC(Input());
  799.       SetMode(Input(), FALSE);
  800.       switch(r)
  801.       {
  802.       case 'a': case 'A':
  803.     ((struct xHookArgs *) (hook->h_Data))->flags |= XADPIF_OVERWRITE;
  804.       case 'y': case 'Y': ret |= XADPIF_OVERWRITE; break;
  805.       case 's': case 'S': ret |= XADPIF_SKIP; break;
  806.       case 'q': case 'Q': ((struct xHookArgs *) (hook->h_Data))->finish = 1; break;
  807.       case 'r': case 'R':
  808.         Printf("\r\033[KEnter new (full) name for '%s':", pi->xpi_FileName);
  809.         Flush(Output());
  810.         FGets(Input(), name, NAMEBUFSIZE-1); /* 1 byte less to correct bug before V39 */
  811.         r = strlen(name);
  812.         if(name[r-1] == '\n') /* skip return character */
  813.           name[--r] = 0;
  814.         Printf("\033[1F\033[K"); /* go up one line and clear it */
  815.         if((pi->xpi_NewName = xadAllocVec(++r, MEMF_PUBLIC)))
  816.         {
  817.           while(r--)
  818.             pi->xpi_NewName[r] = name[r];
  819.           ret |= XADPIF_RENAME;
  820.         }
  821.         else
  822.           Printf("No memory to store new name\n");
  823.       }
  824.     }
  825.     if((pi->xpi_Status & XADPIF_ISDIRECTORY))
  826.     {
  827.       LONG r;
  828.  
  829.       Printf("File '%s' exists as directory, rename? (R|S|\033[1mN\033[0m|Q): ",
  830.       pi->xpi_FileName);
  831.       Flush(Output());
  832.       SetMode(Input(), TRUE);
  833.       r = FGetC(Input());
  834.       SetMode(Input(), FALSE);
  835.       switch(r)
  836.       {
  837.       case 's': case 'S': ret |= XADPIF_SKIP; break;
  838.       case 'q': case 'Q': ((struct xHookArgs *) (hook->h_Data))->finish = 1; break;
  839.       case 'r': case 'R':
  840.         Printf("\r\033[KEnter new (full) name for '%s':", pi->xpi_FileName);
  841.         Flush(Output());
  842.         FGets(Input(), name, NAMEBUFSIZE-1); /* 1 byte less to correct bug before V39 */
  843.         r = strlen(name);
  844.         if(name[r-1] == '\n') /* skip return character */
  845.           name[--r] = 0;
  846.         Printf("\033[1F\033[K"); /* go up one line and clear it */
  847.         if((pi->xpi_NewName = xadAllocVec(++r, MEMF_PUBLIC)))
  848.         {
  849.           while(r--)
  850.             pi->xpi_NewName[r] = name[r];
  851.           ret |= XADPIF_RENAME;
  852.         }
  853.         else
  854.           Printf("No memory to store new name\n");
  855.       }
  856.     }
  857.     if((pi->xpi_Status & XADPIF_MAKEDIRECTORY) &&
  858.     !(ret & XADPIF_MAKEDIRECTORY))
  859.     {
  860.       Printf("Directory of file '%s' does not exist, create? (Y|A|S|\033[1mN\033[0m|Q): ",
  861.       name);
  862.       Flush(Output());
  863.       SetMode(Input(), TRUE);
  864.       switch(FGetC(Input()))
  865.       {
  866.       case 'a': case 'A':
  867.     ((struct xHookArgs *) (hook->h_Data))->flags |= XADPIF_MAKEDIRECTORY;
  868.       case 'y': case 'Y': ret |= XADPIF_MAKEDIRECTORY; break;
  869.       case 's': case 'S': ret |= XADPIF_SKIP; break;
  870.       case 'q': case 'Q': ((struct xHookArgs *) (hook->h_Data))->finish = 1;
  871.       }
  872.       SetMode(Input(), FALSE);
  873.     }
  874.     break;
  875.   case XADPMODE_PROGRESS:
  876.     if(pi->xpi_CurrentSize - ((struct xHookArgs *) (hook->h_Data))->lastprint >= MINPRINTSIZE)
  877.     {
  878.       if(pi->xpi_FileInfo->xfi_Flags & XADFIF_NOUNCRUNCHSIZE)
  879.         Printf("\r\033[KWrote %8lu bytes: %s", pi->xpi_CurrentSize, name);
  880.       else
  881.         Printf("\r\033[KWrote %8lu of %8lu bytes: %s",
  882.         pi->xpi_CurrentSize, pi->xpi_FileInfo->xfi_Size, name);
  883.       Flush(Output());
  884.       ((struct xHookArgs *) (hook->h_Data))->lastprint = pi->xpi_CurrentSize;
  885.     }
  886.     break;
  887.   case XADPMODE_END: Printf("\r\033[KWrote %8ld bytes: %s\n",
  888.     pi->xpi_CurrentSize, name);
  889.     break;
  890.   case XADPMODE_ERROR:
  891.     if(((struct xHookArgs *) (hook->h_Data))->extractmode)
  892.       Printf("\r\033[K%s: %s\n", name, xadGetErrorText(pi->xpi_Error));
  893.     break;
  894. /*
  895.   case XADPMODE_GETINFOEND:
  896.     break;
  897. */
  898.   }
  899.  
  900.   if(!(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)) /* clear ok flag */
  901.     ret |= XADPIF_OK;
  902.  
  903.   return ret;
  904. }
  905.  
  906. void ShowProt(ULONG i)
  907. {
  908.   LONG j;
  909.   UBYTE buf[16], *b = "rwedrwedhsparwed";
  910.   
  911.   for(j = 0; j <= 11; ++j)
  912.     buf[j] = (i & (1<<(15-j))) ? b[j] : '-';
  913.   for(; j <= 15; ++j)
  914.     buf[j] = (i & (1<<(15-j))) ? '-' : b[j];
  915.  
  916.   Printf("%.16s ", buf);
  917. }
  918.  
  919. LONG CheckNameSize(STRPTR name, ULONG size)
  920. {
  921.   LONG ret = 0;
  922.   LONG r;
  923.  
  924.   if((r = strlen(name)) > size)
  925.   {
  926.     UBYTE buf[NAMEBUFSIZE];
  927.  
  928.     Printf("\r\033[KFilename '%s' exceeds name limit of %ld by %ld, rename? (Y|\033[1mN\033[0m|Q): ", name, size, r-size);
  929.  
  930.     Flush(Output());
  931.     SetMode(Input(), TRUE);
  932.     r = FGetC(Input());
  933.     SetMode(Input(), FALSE);
  934.     switch(r)
  935.     {
  936.     case 'q': case 'Q': ret = 1; break;
  937.     case 'y': case 'Y':
  938.       Printf("\r\033[KEnter new name for '%s':", name);
  939.       Flush(Output());
  940.       FGets(Input(), buf, NAMEBUFSIZE-1); /* 1 byte less to correct bug before V39 */
  941.       r = strlen(buf);
  942.       if(buf[r-1] == '\n') /* skip return character */
  943.         buf[--r] = 0;
  944.       Printf("\033[1F\033[K"); /* go up one line and clear it */
  945.       if(!(ret = CheckNameSize(buf, size)))
  946.       {
  947.         for(r = 0; buf[r]; ++r)
  948.           *(name++) = buf[r];
  949.         *name = 0;
  950.       }
  951.       break;
  952.     }
  953.   }
  954.   return ret;
  955. }
  956.  
  957. void CalcPercent(ULONG cr, ULONG ucr, ULONG *p1, ULONG *p2)
  958. {
  959.   ULONG i = 0, j = 0;
  960.  
  961.   if(cr < ucr)
  962.   {
  963.     if(cr > (0xFFFFFFFF/1000))
  964.       i = 1000 - cr / (ucr / 1000);
  965.     else
  966.       i = 1000 - (1000 * cr) / ucr;
  967.     j = i % 10;
  968.     i /= 10;
  969.   }
  970.   *p1 = i;
  971.   *p2 = j;
  972. }
  973.  
  974. #ifndef MULTIFILE
  975. /* would be better to store the pattern parse stuff and do it only once,
  976. but so it is a lot easier */
  977. LONG CheckName(STRPTR *pat, STRPTR name)
  978. {
  979.   UBYTE buf[PATBUFSIZE];
  980.   while(*pat)
  981.   {
  982.     if(ParsePatternNoCase(*(pat++), buf, PATBUFSIZE) >= 0)
  983.     {
  984.       if(MatchPatternNoCase(buf, name))
  985.         return 1;
  986.     } /* A scan failure means no recognition, should be an error print here */
  987.   }
  988.   return 0;
  989. }
  990. #else
  991. STRPTR *GetNames(STRPTR *names)
  992. {
  993.   struct AnchorPath *APath;
  994.   STRPTR *result = 0, s, f, *r;
  995.   ULONG *filelist, *fl = 0, *a, *b, retval = 0, namesize = 0;
  996.   LONG i;
  997.  
  998.   if((APath = (struct AnchorPath *) AllocMem(sizeof(struct AnchorPath)+512, MEMF_PUBLIC|MEMF_CLEAR)))
  999.   {
  1000.     APath->ap_BreakBits = SIGBREAKF_CTRL_C;
  1001.     APath->ap_Strlen = 512;
  1002.  
  1003.     while(*names && !retval)
  1004.     {
  1005.       filelist = 0;
  1006.       for(retval = MatchFirst(*names, APath); !retval; retval = MatchNext(APath))
  1007.       {
  1008.         if(APath->ap_Info.fib_DirEntryType < 0)
  1009.         {
  1010.           i = strlen(APath->ap_Buf)+1;
  1011.           if(!(a = (ULONG *) AllocVec(i+4, MEMF_ANY)))
  1012.             break;
  1013.           CopyMem(APath->ap_Buf, a+1, i);
  1014.           namesize += i;
  1015.           if(!filelist)
  1016.           {
  1017.             filelist = a; *a = 0;
  1018.           }
  1019.           else if(stricmp((STRPTR) (filelist+1), APath->ap_Buf) >= 0)
  1020.           {
  1021.             *a = (ULONG) filelist; filelist = a;
  1022.           }
  1023.           else
  1024.           {
  1025.             for(b = filelist; *b && (i = SDI_stricmp((STRPTR) (*b+4),
  1026.             APath->ap_Buf)) < 0; b = (ULONG *) *b)
  1027.               ;
  1028.             *a = *b; *b = (ULONG) a;
  1029.           }
  1030.         }
  1031.       }
  1032.       if(fl)
  1033.       {
  1034.         for(b = fl; *b; b = (ULONG *) *b)
  1035.           ;
  1036.         *b = (ULONG) filelist;
  1037.       }
  1038.       else
  1039.         fl = filelist;
  1040.       MatchEnd(APath);
  1041.       if(retval == ERROR_NO_MORE_ENTRIES)
  1042.         retval = 0;
  1043.       ++names;
  1044.     }
  1045.  
  1046.     if(!retval)
  1047.     {
  1048.       i = 0;
  1049.       for(b = fl; b; b = (ULONG *) *b)
  1050.         ++i;
  1051.       if((result = (STRPTR *)AllocVec((i+1)*sizeof(STRPTR)+namesize, MEMF_ANY)))
  1052.       {
  1053.         s = ((STRPTR) result)+((i+1)*sizeof(STRPTR));
  1054.         i = 0;
  1055.         for(b = fl; b; b = (ULONG *) *b)
  1056.         {
  1057.           result[i++] = s;
  1058.           for(f = (STRPTR) (b+1); *f; ++f)
  1059.             *(s++) = *f;
  1060.           *(s++) = 0;
  1061.         }
  1062.         result[i] = 0;
  1063.       }
  1064.     }
  1065.  
  1066.     while(fl)
  1067.     {
  1068.       a = (ULONG *) *fl;
  1069.       FreeVec(fl);
  1070.       fl = a;
  1071.     }
  1072.  
  1073.     FreeMem(APath, sizeof(struct AnchorPath)+512);
  1074.   }
  1075.  
  1076.   if(!retval)
  1077.   {
  1078.     Printf("Loading files in following order: ");
  1079.  
  1080.     for(r = result; *r; ++r)
  1081.       Printf("%s%s", *r, r[1] ? ", " : "\n");
  1082.   }
  1083.  
  1084.   return result;
  1085. }
  1086. #endif
  1087.